package fi.pakkanen.yarpg.item;

import fi.pakkanen.yarpg.item.trait.Consumable;
import fi.pakkanen.yarpg.item.trait.Triggerable;

/**
 * The states that an item can be in.
 * 
 * @author Oliver Michels <oliver.michels@gmx.net>
 * 
 */
public enum ItemState {
	PASSIVE {
		@Override
		public boolean changeState(final Item item) {
			return false;
		}
	},
	INACTIVE {
		@Override
		public boolean changeState(final Item item) {
			if (item.isConsumable()) {
				return triggerInactiveConsumable(item);
			} else if (item.isTriggerable()) {
				return triggerInactiveTriggerable(item);
			} else if (item.isEquippable()) {
				return triggerInactiveEquippable(item);
			}
			return false;
		}
	},
	ACTIVE {
		@Override
		public boolean changeState(final Item item) {
			item.revert();
			triggerRechargingWhenNecessary(item);
			return true;
		}
	},
	EQUIPPED {
		@Override
		public boolean changeState(final Item item) {
			item.setState(INACTIVE);
			return true;
		}
	},
	RECHARGING {
		@Override
		public boolean changeState(final Item item) {
			item.setState(INACTIVE);
			return true;
		}
	},
	CONSUMED {
		@Override
		public boolean changeState(final Item item) {
			return false;
		}
	},
	;

	/**
	 * Trigger the state change for a given inactive consumable item.
	 * 
	 * @param item
	 *            the given item
	 * @return true when the state could be changed, false otherwise
	 */
	private static boolean triggerInactiveConsumable(final Item item) {
		if (item.invoke()) {
			((Consumable) item).reduceCharges();

			if (((Consumable) item).isConsumed()) {
				item.setState(CONSUMED);
			} else {
				triggerActive(item);
			}
			return true;
		}
		return false;
	}

	/**
	 * Trigger the state change for a given inactive triggerable item.
	 * 
	 * @param item
	 *            the given item
	 * @return true when the state could be changed, false otherwise
	 */
	private static boolean triggerInactiveTriggerable(final Item item) {
		if (item.invoke()) {
			triggerActive(item);
			return true;
		}

		return false;
	}

	/**
	 * Trigger the state change for a given inactive equippable item.
	 * 
	 * @param item
	 *            the given item
	 * @return true when the state could be changed, false otherwise
	 */
	private static boolean triggerInactiveEquippable(final Item item) {
		if (item.invoke()) {
			item.setState(EQUIPPED);
			return true;
		}
		return false;
	}

	/**
	 * Trigger the active state when possible. Otherwise check for the
	 * re-charging state.
	 * 
	 * @param item
	 *            the given item
	 * @return
	 */
	private static void triggerActive(final Item item) {
		if (((Triggerable) item).hasDuration()) {
			item.setState(ACTIVE);
		} else {
			triggerRechargingWhenNecessary(item);
		}
	}

	/**
	 * Trigger the re-charging state for an item when it needs to be re-charged.
	 * Othwise switch to inactive state.
	 * 
	 * @param item
	 *            the given item
	 */
	private static void triggerRechargingWhenNecessary(final Item item) {
		if (item.isConsumable() && ((Consumable) item).isConsumed()) {
			item.setState(CONSUMED);
		} else if (((Triggerable) item).needsRecharging()) {
			item.setState(RECHARGING);
		} else {
			item.setState(INACTIVE);
		}
	}

	/**
	 * Change the state for a given item.
	 * 
	 * @param item
	 *            the given item
	 * @return true when the state could be changed, false otherwise
	 */
	public abstract boolean changeState(Item item);

}
